package com.inmotion.android.sdk.moor7;

import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;

import com.moor.imkf.IMChat;
import com.moor.imkf.IMChatManager;
import com.moor.imkf.IMMessage;
import com.moor.imkf.db.dao.MessageDao;
import com.moor.imkf.lib.MoorBaseLibManager;
import com.moor.imkf.listener.ChatListener;
import com.moor.imkf.listener.InitListener;
import com.moor.imkf.listener.OnSessionBeginListener;
import com.moor.imkf.listener.OnSubmitOfflineMessageListener;
import com.moor.imkf.model.entity.FromToMessage;
import com.moor.imkf.requesturl.RequestUrl;
import com.moor.imkf.utils.MoorUtils;
import com.moor.imkf.utils.YKFUtils;

import org.json.JSONObject;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.flutter.plugin.common.MethodChannel;

public class Moo7Helper {

    private static final String TAG = "Moo7Helper";

    public static final String ACTION_MSG_BROADCAST_RECEIVER = "com.m7.imkf.KEFU_NEW_MSG";

    private MethodChannel channel;
    private IApp appDelegate;
    private final Moor7Context moor7Context;

    private static Moo7Helper sInstance;

    public static Moo7Helper getInstance() {
        if (sInstance == null) sInstance = new Moo7Helper();
        return sInstance;
    }

    private Moo7Helper() {
        moor7Context = new Moor7Context();
    }

    public void setChannel(MethodChannel channel) {
        this.channel = channel;
    }

    public void setAppDelegate(IApp appDelegate0) {
        appDelegate = appDelegate0;
    }

    public void callFlutter(String method, Map<String, String> params) {
        Log.d(TAG, "callFlutter: method->" + method + ", params->" + params);
        channel.invokeMethod(method, params);
    }

    public void callFlutter(String method) {
        Log.d(TAG, "callFlutter: method->" + method);
        channel.invokeMethod(method, null);
    }

    public void setRequestBaseUrl(int type, MethodChannel.Result result) {
        if (type == 0) type = RequestUrl.ALIYUN_REQUEST;
        else if (type == 1) type = RequestUrl.TENCENT_REQUEST;
        MoorBaseLibManager.getInstance().initBaseLib(appDelegate.getApp());
        RequestUrl.setRequestBasic(type);
        result.success(FlutterRsp.success().toMap());
    }

    public void initSDK(Context appContext, String accessId, String username, String userId) {
        IMChatManager.setUSE_ForegroundService(false);

        moor7Context.accessId = accessId;
        moor7Context.username = username;
        moor7Context.userId = userId;
        
        YKFUtils.init(appDelegate.getApp());
        IMChatManager.getInstance().setImageLoader(new GlideImageLoader());

        IMChatManager.getInstance().setOnInitListener(new InitListener() {
            @Override
            public void oninitStart() {
            }

            @Override
            public void oninitSuccess() {
                callFlutter(Constant.cbInitSuccess, null);
            }

            @Override
            public void onInitFailed(int i) {
                Log.d(TAG, "onInitFailed: " + i);
                if (i == 1010) {
                    // 重复初始化，认为初始化成功
                    oninitSuccess();
                } else {
                    callFlutter(Constant.cbInitFailed, buildParams("error", String.valueOf(i)));
                }
            }
        });
        // 如果在未调用 initSDK 初始化前调用 MessageDao.getInstance().xxx 方法，会导致在后续调用initSDK时在resetDbStatus()时异常，造成无法初始化的问题
        // 所以在每次IMChatManager.getInstance().init()前，将MessageDao中的instance释放，使用正确的参数(主要为context)重新生成一次MessageDao的单例实例
        releaseMessageDaoInstance();
        IMChatManager.getInstance().init(ACTION_MSG_BROADCAST_RECEIVER, accessId, username, userId);
    }

    public void quitSDK() {
        IMChatManager.getInstance().quitSDk();
    }

    public void enableChat(String peerId, JSONObject params, MethodChannel.Result result) {
        Log.d(TAG, "enableChat: peer id: " + peerId + ", params: " + params.toString());
        IMChatManager.getInstance().setUserOtherParams(null, params, false, null);
        IMChatManager.getInstance().beginSession(peerId, new OnSessionBeginListener() {
            @Override
            public void onSuccess(String s) {
                Log.d(TAG, "enableChat onSuccess: " + s);
                result.success(FlutterRsp.success(s).toMap());
            }

            @Override
            public void onFailed(String s) {
                Log.d(TAG, "enableChat onFailed: " + s);
                result.success(FlutterRsp.error(s).toMap());
            }
        });
    }

    public void sendTextMessage(String message, MethodChannel.Result result) {
        sendMessage(IMMessage.createTxtMessage(message), new ChatListener() {

            @Override
            public void onSuccess(String s) {
                Log.d(TAG, "sendTextMessage onSuccess: " + s);
                result.success(FlutterRsp.success(s).toMap());
                notifyNewMessage();
            }

            @Override
            public void onFailed(String s) {
                Log.d(TAG, "sendTextMessage onFailed: " + s);
                result.success(FlutterRsp.error(s).toMap());
                notifyNewMessage();
            }

            @Override
            public void onProgress(int i) {
            }
        });
    }

    public void sendImageMessage(String path, MethodChannel.Result result) {
        sendMessage(IMMessage.createImageMessage(path), new ChatListener() {

            @Override
            public void onSuccess(String s) {
                result.success(FlutterRsp.success(s).toMap());
                notifyNewMessage();
            }

            @Override
            public void onFailed(String s) {
                result.success(FlutterRsp.error(s).toMap());
                notifyNewMessage();
            }

            @Override
            public void onProgress(int i) {
            }
        });
    }

    public void sendFileMessage(String filePath, String fileName, String fileSize, MethodChannel.Result result) {
        sendMessage(IMMessage.createFileMessage(filePath, fileName, fileSize, ""), new ChatListener() {

            @Override
            public void onSuccess(String s) {
                result.success(FlutterRsp.success(s).toMap());
                notifyNewMessage();
            }

            @Override
            public void onFailed(String s) {
                result.success(FlutterRsp.error(s).toMap());
                notifyNewMessage();
            }

            @Override
            public void onProgress(int i) {
            }
        });
    }

    private void sendMessage(FromToMessage msg, ChatListener chatListener) {
        IMChat.getInstance().sendMessage(msg, chatListener);
        notifyNewMessage();
    }

    public void resendMessage(String messageId, MethodChannel.Result result) {
        FromToMessage msg = getMessage(messageId);
        if (msg == null) {
            result.success(FlutterRsp.error("Message [" + messageId + "] is not found.").toMap());
            return;
        }
        IMChat.getInstance().sendMessage(msg, new ChatListener() {
            @Override
            public void onSuccess(String s) {
                result.success(FlutterRsp.success(s).toMap());
                notifyNewMessage();
            }

            @Override
            public void onFailed(String s) {
                result.success(FlutterRsp.error(s).toMap());
                notifyNewMessage();
            }

            @Override
            public void onProgress(int i) {
            }
        });
        notifyNewMessage();
    }

    public void deleteLocalMessage(String msgId, MethodChannel.Result result) {
        MessageDao.getInstance().deleteMsg(getMessage(msgId));
        result.success(FlutterRsp.success("").toMap());
    }

    public void getMessage(String msgId, MethodChannel.Result result) {
        FromToMessage msg = getMessage(msgId);
        result.success(FlutterRsp.success(encodeMessage(msg)).toMap());
    }

    public FromToMessage getMessage(String msgId) {
        return MessageDao.getInstance().getMessageById(msgId);
    }

    public void getUnreadMessageCount(String accessId, String userName, String userId, MethodChannel.Result result) {
        IMChatManager.getInstance().getMsgUnReadCountFromService(accessId, userName, userId, count -> result.success(FlutterRsp.success(count).toMap()));
    }

    public void getUnreadMessageList(String afterMsgId, MethodChannel.Result result) {
        List<Map<String, String>> list = new ArrayList<>();
        if (!TextUtils.isEmpty(afterMsgId)) {
            List<FromToMessage> msgList = new ArrayList<>();
            getUnreadMessageListImpl(1, afterMsgId, msgList);
            for (FromToMessage msg : msgList) list.add(encodeMessage(msg));
        }
        result.success(FlutterRsp.success(list).toMap());
    }

    /**
     * 获取指定消息后的未读消息列表
     *
     * @param page       当前页
     * @param afterMsgId 指定消息后的未读消息
     * @param output     消息列表
     * @return 是否还需要继续获取列表
     */
    private boolean getUnreadMessageListImpl(int page, String afterMsgId, List<FromToMessage> output) {
        List<FromToMessage> msgList = IMChatManager.getInstance().getMessages(page);
        if (msgList == null || msgList.isEmpty()) return false;
        boolean reachedThresholdMsg = false;
        for (FromToMessage msg : msgList) {
            // msgList按时间倒序
            // 只取afterMsgId那条之后的
            if (TextUtils.equals(msg._id, afterMsgId)) {
                reachedThresholdMsg = true;
                break;
            }
            output.add(msg);
        }
        // 最多查询3页
        if (page >= 3 || reachedThresholdMsg) return false;
        return getUnreadMessageListImpl(page + 1, afterMsgId, output);
    }

    public void leaveMessage(String peerId, String message, MethodChannel.Result result) {
        IMChatManager.getInstance().submitOfflineMessage("", "", peerId, message, null, null, new OnSubmitOfflineMessageListener() {
            @Override
            public void onSuccess() {
                result.success(FlutterRsp.success().toMap());
            }

            @Override
            public void onFailed() {
                result.success(FlutterRsp.error("").toMap());
            }
        });
    }

    public void getMessageList(int page, MethodChannel.Result result) {
        List<FromToMessage> msgList = IMChatManager.getInstance().getMessages(page);
        List<Map<String, String>> list = new ArrayList<>();
        for (FromToMessage msg : msgList) list.add(encodeMessage(msg));
        result.success(FlutterRsp.success(list).toMap());
    }

    public void notifyNewMessage() {
        callFlutter(Constant.cbNewMessage);
    }

    public void notifyChatTaken() {
        callFlutter(Constant.cbCustomClaim);
    }

    public void notifyServiceOnline() {
        callFlutter(Constant.cbCustomOnline);
    }

    public void notifyServiceOffline() {
        callFlutter(Constant.cbCustomOffline);
    }

    public void notifyRobotMessage() {
        callFlutter(Constant.cbRobotAction);
    }

    public void notifyServiceFinish() {
        callFlutter(Constant.cbCustomFinish);
    }

    public void notifyQueueUpdated(int queueNum) {
        callFlutter(Constant.cbCustomQueue, buildParams("num", String.valueOf(queueNum)));
    }

    public void notifyServiceInfoUpdate(String type, String exten, String username, String avatar) {
        callFlutter(Constant.cbCustomAgentInfo, buildParams("type", type, "exten", exten, "username", username, "avatar", avatar));
    }

    public void notifyVIPService() {
        callFlutter(Constant.cbCustomVIP);
    }

    public void notifyLeaveMsg(String id, String peerId) {
        callFlutter(Constant.cbCustomLeaveMsg, buildParams("id", id, "peerId", peerId));
    }


    public void notifyServiceRecallMsg() {
        callFlutter(Constant.cbRecallMessageStatus);
    }

    public void notifyServiceEvaluation() {
        callFlutter(Constant.cbCustomInvestigate);
    }

    public void notifyServiceInputting() {
        callFlutter(Constant.cbChangeCustomStatus);
    }

    public static Map<String, String> encodeMessage(FromToMessage msg) {
        Map<String, String> map = new HashMap<>();
        map.put("id", msg._id);
        map.put("text", msg.message);
        // map.put("type", msg.type);
        map.put("messageType", msg.msgType);
        map.put("sessionId", msg.sessionId);
        map.put("userId", msg.userId);
        map.put("accessId", msg.accessId);
        map.put("createdTime", String.valueOf(msg.when));
        map.put("fromType", msg.userType);
        // 0 - 成功；1 - 失败；2 - 发送中
        String status = "1";
        if (TextUtils.equals("true", msg.sendState)) {
            status = "0";
        } else if (TextUtils.equals("sending", msg.sendState)) {
            status = "2";
        }
        map.put("status", status);

        map.put("recordSeconds", String.valueOf((int) msg.recordTime));
        map.put("localFilePath", msg.filePath);
        map.put("remoteFilePath", msg.message);
        map.put("fileName", msg.fileName);
        map.put("fileSize", msg.fileSize);
        map.put("mp3FileSize", "0");
        map.put("downloadState", msg.fileDownLoadStatus);
        map.put("agentExten", msg.exten);
        map.put("agentName", msg.displayName);
        map.put("agentIcon", msg.im_icon);
        map.put("isRobot", String.valueOf(msg.isRobot ? 1 : 2));
        map.put("userType", msg.userType);
        map.put("messageStatus", "0");
        map.put("idRead", msg.unread);
        map.put("voiceRead", msg.unread2);
        map.put("platform", "android");
        map.put("device", Build.MODEL);

        return map;
    }

    public static FromToMessage decodeMessage(Map<String, String> map) {
        FromToMessage msg = new FromToMessage();
        msg._id = map.get("id");
        msg.message = map.get("text");
        msg.msgType = map.get("messageType");
        msg.sessionId = map.get("sessionId");
        msg.userId = map.get("userId");
        msg.accessId = map.get("accessId");
        msg.timeStamp = map.get("createTime");
        msg.userType = map.get("fromType");
        // true - 成功；false - 失败；sending - 发送中
        String status = "false";
        if (TextUtils.equals("0", msg.sendState)) {
            status = "true";
        } else if (TextUtils.equals("2", msg.sendState)) {
            status = "sending";
        }
        map.put("status", status);
        msg.sendState = map.get("status");
        msg.recordTime = Float.parseFloat(map.get("recordSeconds"));
        msg.filePath = map.get("localFilePath");
        msg.fileName = map.get("fileName");
        msg.fileSize = map.get("fileSize");
        msg.fileDownLoadStatus = map.get("downloadState");
        msg.exten = map.get("agentExten");
        msg.displayName = map.get("agentName");
        msg.im_icon = map.get("agentIcon");
        msg.isRobot = "1".equals(map.get("isRobot"));
        msg.userType = map.get("userType");
        msg.unread = map.get("idRead");
        msg.unread2 = map.get("voiceRead");

        return msg;
    }

    private Map<String, String> buildParams(String... params) {
        Map<String, String> map = new HashMap<>();
        for (int i = 0, count = params.length / 2; i < count; i++) {
            map.put(params[i * 2], params[(i * 2) + 1]);
        }
        return map;
    }

    private static void releaseMessageDaoInstance() {
        try {
            Field field = MessageDao.class.getDeclaredField("instance");
            field.setAccessible(true);
            field.set(null, null);
            if (field.get(null) == null) {
                Log.d(TAG, "releaseMessageDaoInstance: MessageDao private instance was released!");
            }
        } catch (Exception e) {
            Log.d(TAG, "releaseMessageDaoInstance: Error occurred!");
            e.printStackTrace();
        }
    }

    private static class Moor7Context {
        String accessId;
        String username;
        String userId;

        Moor7Context() {
        }

        Moor7Context(String accessId, String username, String userId) {
            this.accessId = accessId;
            this.username = username;
            this.userId = userId;
        }
    }
}
